<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<style>
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote {
    margin: 0;
    padding: 0;
}
body {
    font-family: "Helvetica Neue", Helvetica, "Hiragino Sans GB", Arial, sans-serif;
    font-size: 13px;
    line-height: 18px;
    color: #737373;
    background-color: white;
    margin: 10px 13px 10px 13px;
}
table {
	margin: 10px 0 15px 0;
	border-collapse: collapse;
}
td,th {	
	border: 1px solid #ddd;
	padding: 3px 10px;
}
th {
	padding: 5px 10px;	
}

a {
    color: #0069d6;
}
a:hover {
    color: #0050a3;
    text-decoration: none;
}
a img {
    border: none;
}
p {
    margin-bottom: 9px;
}
h1,
h2,
h3,
h4,
h5,
h6 {
    color: #404040;
    line-height: 36px;
}
h1 {
    margin-bottom: 18px;
    font-size: 30px;
}
h2 {
    font-size: 24px;
}
h3 {
    font-size: 18px;
}
h4 {
    font-size: 16px;
}
h5 {
    font-size: 14px;
}
h6 {
    font-size: 13px;
}
hr {
    margin: 0 0 19px;
    border: 0;
    border-bottom: 1px solid #ccc;
}
blockquote {
    padding: 13px 13px 21px 15px;
    margin-bottom: 18px;
    font-family:georgia,serif;
    font-style: italic;
}
blockquote:before {
    content:"\201C";
    font-size:40px;
    margin-left:-10px;
    font-family:georgia,serif;
    color:#eee;
}
blockquote p {
    font-size: 14px;
    font-weight: 300;
    line-height: 18px;
    margin-bottom: 0;
    font-style: italic;
}
code, pre {
    font-family: Monaco, Andale Mono, Courier New, monospace;
}
code {
    background-color: #fee9cc;
    color: rgba(0, 0, 0, 0.75);
    padding: 1px 3px;
    font-size: 12px;
    -webkit-border-radius: 3px;
    -moz-border-radius: 3px;
    border-radius: 3px;
}
pre {
    display: block;
    padding: 14px;
    margin: 0 0 18px;
    line-height: 16px;
    font-size: 11px;
    border: 1px solid #d9d9d9;
    white-space: pre-wrap;
    word-wrap: break-word;
}
pre code {
    background-color: #fff;
    color:#737373;
    font-size: 11px;
    padding: 0;
}
sup {
    font-size: 0.83em;
    vertical-align: super;
    line-height: 0;
}
* {
	-webkit-print-color-adjust: exact;
}
@media screen and (min-width: 914px) {
    body {
        width: 854px;
        margin:10px auto;
    }
}
@media print {
	body,code,pre code,h1,h2,h3,h4,h5,h6 {
		color: black;
	}
	table, pre {
		page-break-inside: avoid;
	}
}
</style>
<title>全新的动画</title>

</head>
<body>
<h2>全新的动画</h2>

<p>在Material Design设计中，为用户与app交互反馈他们的动作行为和提供了视觉上的连贯性。Material主题为控件和Activity的过渡提供了一些默认的动画，在android  L上，允许自定义这些动画：</p>

<ol>
<li>Touch feedback  触摸反馈</li>
<li>Circular Reveal  圆形展示</li>
<li>Curved motion       曲线运动</li>
<li>View state changes  视图状态变化</li>
<li>Vector Drawables  矢量图动画</li>
<li>Activity transitions  活动转场</li>
</ol>


<h3>触摸反馈</h3>

<p>触摸反馈是指用户在触摸控件时的一种可视化交互，在Android L之前，通常是通过press色变来凸显，但是因为是瞬间变化的效果，不如动画生动。</p>

<p>在Android L使用了RippleDrawable类，用一个水波纹扩散效果在两种不同的状态间过渡。</p>

<p>使用Material Design样式的应用，button默认带有该效果。除了默认的效果外，系统还提供了另外两种效果，我们只把button的背景指定为：</p>

<ol>
<li><code>?android:attr/selectableItemBackground</code></li>
<li><code>?android:attr/selectableItemBackgroundBorderless</code></li>
</ol>


<p>任何view处于可点击状态，都可以使用RippleDrawable来达到水波纹特效。</p>

<p>我们也可以通过设置RippleDrawable的颜色属性来调节动画颜色，系统默认的颜色为主题的一个属性颜色：android:colorControlHighlight，所以我们可以通过修改该颜色值来统一修改默认的水波纹颜色。android:colorAccent可以修改checkbox的选中颜色，更多颜色设置请参考主题。</p>

<p>系统的三种触摸反馈都是通过xml构建的，内容如下：</p>

<pre><code>默认：
&lt;ripple xmlns:android="http://schemas.android.com/apk/res/android"
        android:color="?android:attr/colorControlHighlight"&gt;
    &lt;item&gt;
        &lt;inset
               android:insetLeft="4dp"
               android:insetTop="6dp"
               android:insetRight="4dp"
               android:insetBottom="6dp"&gt;
            &lt;shape android:shape="rectangle"&gt;
                &lt;corners android:radius="2dp" /&gt;
                &lt;solid android:color="?android:attr/colorButtonNormal" /&gt;
                &lt;padding android:left="8dp"
                         android:top="4dp"
                         android:right="8dp"
                         android:bottom="4dp" /&gt;
            &lt;/shape&gt;
        &lt;/inset&gt;
    &lt;/item&gt;
&lt;/ripple&gt;


?android:attr/selectableItemBackground
&lt;ripple xmlns:android="http://schemas.android.com/apk/res/android"
        android:color="?android:attr/colorControlHighlight"&gt;
    &lt;item android:id="@android:id/mask"&gt;
        &lt;color android:color="@android:color/white" /&gt;
    &lt;/item&gt;
&lt;/ripple&gt;

?android:attr/selectableItemBackgroundBorderless
&lt;ripple xmlns:android="http://schemas.android.com/apk/res/android"
        android:color="?android:attr/colorControlHighlight"
        /&gt;

代码中设置
RippleDrawableColorStateList stateList = getResources().getColorStateList(R.color.tint_state_color);
RippleDrawable rippleDrawable = new RippleDrawable(stateList,null,null);
view.setBackground(rippleDrawable);
</code></pre>

<h3>圆形展示</h3>

<p>我们通常会显示或者隐藏一个view，在Android L之前，这是一个生硬瞬间变化动作，现在，有了一个新的api为此效果提供一个圆形的显示或者隐藏的动画效果。</p>

<p>RevealAnimator和之前的动画使用没什么区别，同样可以设置监听器和加速器来实现各种各样的特效，该动画主要用在隐藏或者显示一个view，改变view的大小等过渡效果。</p>

<p>通过<code>ViewAnimationUtils.createCircularReveal</code>来创建一个动画，该api接受5个参数：</p>

<ol>
<li>view 操作的视图</li>
<li>centerX 动画开始的中心点X</li>
<li>centerY 动画开始的中心点Y</li>
<li>startRadius 动画开始半径</li>
<li>startRadius 动画结束半径</li>
</ol>


<p>沿着中心的缩小的动画</p>

<pre><code>Animator animator = ViewAnimationUtils.createCircularReveal(view, view.getWidth() / 2, view.getHeight() / 2, view.getWidth(), 0);
animator.setInterpolator(new LinearInterpolator());
animator.setDuration(1000);
animator.start();
</code></pre>

<p>从左上角扩展的圆形动画</p>

<pre><code>Animator animator = ViewAnimationUtils.createCircularReveal(view,0,0,0,(float) Math.hypot(view.getWidth(), view.getHeight()));
animator.setDuration(1000);
animator.start();
</code></pre>

<h3>曲线运动</h3>

<p>曲线动画在Android L之前我们可以通过继承位移动画重载applyTransformation函数来实现运动轨迹算法，但是操作起来比较繁琐：</p>

<p>通过继承位移动画，来改写applyTransformation来修改位移的轨迹    </p>

<p>通过继承位移动画，来改写applyTransformation来修改位移的轨迹    </p>

<pre><code>public class ArcTranslateAnimation extends Animation {
    private float mFromXValue,mToXValue,mFromYValue,mToYValue;
    private float mFromXDelta,mToXDelta,mFromYDelta,mToYDelta;
    private PointF mStart,mControl,mEnd;
    public ArcTranslateAnimation(float fromXValue, float toXValue, float fromYValue, float toYValue) {
        mFromXValue = fromXValue;
        mToXValue = toXValue;
        mFromYValue = fromYValue;
        mToYValue = toYValue;
    }
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        float dx = calcBezier(interpolatedTime, mStart.x, mControl.x, mEnd.x);
        float dy = calcBezier(interpolatedTime, mStart.y, mControl.y, mEnd.y);
        t.getMatrix().setTranslate(dx, dy);
    }
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        mFromXDelta = resolveSize(ABSOLUTE, mFromXValue, width, parentWidth);
        mToXDelta = resolveSize(ABSOLUTE, mToXValue, width, parentWidth);
        mFromYDelta = resolveSize(ABSOLUTE, mFromYValue, height, parentHeight);
        mToYDelta = resolveSize(ABSOLUTE, mToYValue, height, parentHeight);
        mStart = new PointF(mFromXDelta, mFromYDelta);
        mEnd = new PointF(mToXDelta, mToYDelta);
        mControl = new PointF(mFromXDelta, mToYDelta);
    }
    private long calcBezier(float interpolatedTime, float p0, float p1, float p2) {
        return Math.round((Math.pow((1 - interpolatedTime), 2) * p0) 
        + (2 * (1 - interpolatedTime) * interpolatedTime * p1) + 
        (Math.pow(interpolatedTime, 2) * p2);
    }
}
</code></pre>

<p>现在我们有了更简单的实现方式。</p>

<p>ObjectAnimator新增了path方式来构建动画，并且可以同时对x，y两个属性做动画，我们只用指定一个曲线的path，即可作出曲线的动画，可以用quadTo/cubicTo绘制贝塞尔曲线，也可以使用arcTo绘制普通的弧线
新增了PathInterpolator动画插入器，新的基于贝塞尔曲线或路径对象的插入器。这个插入器指定了一个<code>1x1</code>正方形运动曲线，它使用(0,0)为锚点，(1,1)为控制点，作为构造函数的参数。</p>

<pre><code>Path path = new Path();
path.moveTo(100,100);
path.quadTo(1000,300,300,700);
ObjectAnimator mAnimator = ObjectAnimator.ofFloat(curved, View.X, View.Y, path);
Path p = new Path();
p.lineTo(0.6f, 0.9f);
p.lineTo(0.75f, 0.2f);
p.lineTo(1f, 1f);
mAnimator.setInterpolator(new PathInterpolator(p));
mAnimator.setDuration(3000);
mAnimator.start();
</code></pre>

<h3>视图状态变化</h3>

<p>Android L在原有的图片选择器和颜色选择器上进行了增强，不仅是控件能根据不同的状态显示不同的背景图片，还能在两种状态切换时指定一个动画，来增加过渡效果，吸引用户眼球，以突出重点内容。</p>

<p>StateListAnimator类和图片选择器，颜色选择器类似，可以根据view的状态改变呈现不同的动画效果，通过xml我们可以构建对应不同状态的动画合集，其使用方式也非常简单，在对应的状态指定一个属性动画即可：</p>

<pre><code>&lt;selector xmlns:android="http://schemas.android.com/apk/res/android"&gt;
    &lt;item android:state_pressed="true"&gt;
        &lt;set&gt;
            &lt;objectAnimator android:propertyName="translationZ"
                            android:duration="200"
                            android:valueTo="20dp"
                            android:valueType="floatType"/&gt;
        &lt;/set&gt;
    &lt;/item&gt;
    &lt;item android:state_enabled="true" android:state_pressed="false"&gt;
        &lt;set&gt;
            &lt;objectAnimator android:propertyName="translationZ"
                            android:duration="200"
                            android:valueTo="0"
                            android:valueType="floatType"/&gt;
        &lt;/set&gt;
    &lt;/item&gt;
&lt;/selector&gt;
</code></pre>

<p>在xml中通过<code>android:stateListAnimator</code>来指定状态动画，代码中可以通过AnimationInflater.loadStateListAnimator()加载动画，并使用View.setStateListAnimator()将其指定给View。</p>

<p>可以在状态切换的过程指定多个属性动画的合集，继承了Material主题后，按钮默认拥有了z属性动画。如果想取消这种默认状态，可以把状态动画指定为null。</p>

<p>除了StateListAnimator类指定状态切换的属性动画外，还可以通过AnimatedStateListDrawable来指定状态切换的帧动画：</p>

<pre><code>&lt;animated-selector xmlns:android="http://schemas.android.com/apk/res/android"&gt;
    &lt;item android:id="@+id/pressed" android:drawable="@drawable/btn_check_15" android:state_pressed="true"/&gt;
    &lt;item android:id="@+id/normal"  android:drawable="@drawable/btn_check_0"/&gt;
    &lt;transition android:fromId="@+id/normal" android:toId="@+id/pressed"&gt;
        &lt;animation-list&gt;
            &lt;item android:duration="20" android:drawable="@drawable/btn_check_0"/&gt;
            &lt;item android:duration="20" android:drawable="@drawable/btn_check_1"/&gt;
            &lt;item android:duration="20" android:drawable="@drawable/btn_check_2"/&gt;
        &lt;/animation-list&gt;
    &lt;/transition&gt;
&lt;/animated-selector&gt;
</code></pre>

<h3>矢量图动画</h3>

<p>前面我们学习了矢量图，AnimatedVectorDrawable类让你能使一个矢量图动起来。矢量图动画比帧动画更平滑的展现图片的变化过程，并且无论在内存占用，还是包体积占用上都要优于帧动画。通常定义一个矢量图动画需要三步：</p>

<p>在drawable资源目录下定义一个矢量图</p>

<pre><code>&lt;vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:height="64dp"
    android:width="64dp"
    android:viewportHeight="600"
    android:viewportWidth="600"&gt;
    &lt;group
        android:name="rotationGroup"
        android:pivotX="300.0"
        android:pivotY="300.0"
        android:rotation="45.0" &gt;
        &lt;path
            android:name="v"
            android:fillColor="#000000"
            android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" /&gt;
    &lt;/group&gt;
&lt;/vector&gt;
</code></pre>

<p>在anim下顶一个objectAnimator，并在动画中修改矢量图的path</p>

<pre><code>&lt;set xmlns:android="http://schemas.android.com/apk/res/android"&gt;
    &lt;objectAnimator
        android:duration="3000"
        android:propertyName="pathData"
        android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z"
        android:valueTo="M300,70 l 0,-70 70,0  0,140 -70,0z"
        android:valueType="pathType" /&gt;
&lt;/set&gt;
</code></pre>

<p>在drawable下定义一个animated-vector，并把drawable指向矢量图，把target中的动画指定为之前定义的objectAnimator</p>

<pre><code>&lt;animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/vector_drawable" &gt;
    &lt;target
        android:name="v"
        android:animation="@anim/vector_anim" /&gt;
&lt;/animated-vector&gt;
</code></pre>

<h3>转场动画</h3>

<p>在Android L之前，我们可以在startActivity之后调用overridePendingTransition来指定Activity的转场动画。现在Android L给我们带来了更绚丽的转场动画。</p>

<p>新的转场动画分为两大类，一种是普通的过渡动画，另一种是共享元素的过渡动画。
要想使用新的转场动画，可以继承Material Design主题后在style风格中指定：</p>

<pre><code>&lt;style name="DefaultTheme" parent="android:Theme.Material"&gt;
    &lt;!-- 允许使用transitions --&gt;
    &lt;item name="android:windowContentTransitions"&gt;true&lt;/item&gt;
    &lt;!-- 指定进入、退出、返回、重新进入时的transitions --&gt;
    &lt;item name="android:windowEnterTransition"&gt;@transition/explode&lt;/item&gt;
    &lt;item name="android:windowExitTransition"&gt;@transition/explode&lt;/item&gt;
    &lt;item name="android:windowReturnTransition"&gt;@transition/explode&lt;/item&gt;
    &lt;item name="android:windowReenterTransition"&gt;@transition/explode&lt;/item&gt;
    &lt;!-- 指定进入、退出、返回、重新进入时的共享transitions --&gt;
    &lt;item name="android:windowSharedElementEnterTransition"&gt;@transition/change&lt;/item&gt;
    &lt;item name="android:windowSharedElementExitTransition"&gt;@transition/change&lt;/item&gt;
    &lt;item name="android:windowSharedElementReturnTransition"&gt;@transition/change&lt;/item&gt;
    &lt;item name="android:windowSharedElementReenterTransition"&gt;@transition/change&lt;/item&gt;
&lt;/style&gt;
</code></pre>

<p>也可以在activity的oncreate方法中进行代码设置：</p>

<pre><code>// 允许使用transitions
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
// 指定进入、退出、返回、重新进入时的transitions
getWindow().setEnterTransition(new Explode());
getWindow().setExitTransition(new Explode());
getWindow().setEnterTransition(new Explode());
getWindow().setExitTransition(new Explode());
// 指定进入、退出、返回、重新进入时的共享transitions
getWindow().setSharedElementEnterTransition(new ChangeTransform());
getWindow().setSharedElementExitTransition(new ChangeTransform());
getWindow().setSharedElementReturnTransition(new ChangeTransform());
getWindow().setSharedElementReenterTransition(new ChangeTransform());
</code></pre>

<h6>普通转场动画</h6>

<p>所有继承自visibility类都可以作为进入、退出的过度动画。如果我们想自定义进入和退出时的动画效果，只需要继承Visibility，重载onAppear和onDisappear方法来定义进入喝退出的动画。系统提供了三种默认方式：</p>

<ol>
<li>explode  从屏幕中心移入或移出视图</li>
<li>slide    从屏幕边缘移入或移出视图</li>
<li>fade 改变视图的透明度</li>
</ol>


<p>想在xml中指定自定义的进入、退出的过度动画需要先对动画进行定义：</p>

<pre><code>&lt;transition class="my.app.transition.CustomTransition"/&gt;
</code></pre>

<p>注意：其中CustomTransition是你自定义的动画，它必须继承自Visibility。</p>

<p>想以普通转场动画的方式启动一个Activity，必须在startActivity函数中传递一个ActivityOptions的Bundle对象：</p>

<pre><code>ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(activity);  
startActivity(intent, options.toBundle());  
</code></pre>

<p>如果想让返回也具备转场效果，那么在返回的Activity中不要再调用<code>finish</code>函数，而是应该使用<code>finishAfterTransition</code>来结束一个Activity，该函数会等待动画执行完毕才结束该Activity。</p>

<h6>共享转场动画</h6>

<p>当两个Activity具备某些相遇的元素时，共享转场动画将是一个非常好的选择。使用转场动画需要将相同的元素通过<code>android:transitionName</code>或者<code>view.setTransitionName</code>设置为相同的名称，这样系统才能区分出相同的元素。</p>

<p>共享转场动画支持以下共享元素：</p>

<ol>
<li>changeBounds 对目标视图的大小进行动画</li>
<li>changeClipBounds 对目标视图的剪裁大小进行动画</li>
<li>changeTransform 对目标视图进行缩放、旋转、位移动画</li>
<li>changeImageTransform 对目标图片进行缩放</li>
</ol>


<p>通过下面的函数启动一个共享元素动画：</p>

<pre><code>ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(activity, view, "name");  
startActivity(intent, options.toBundle()); 
</code></pre>

<p>如果有多个共享元素，则可以通过Pair进行包装处理：</p>

<pre><code>ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(activity,Pair.create(view1, "name1"),Pair.create(view2, "name2"));      
startActivity(intent,.toBundle());
</code></pre>

<p>返回时如果需要具备转场动画，那么也需要用<code>finish</code>函数替代<code>finishAfterTransition</code>来结束一个Activity。</p>

<p>共享转场动画通常可以根据指定的元素判断出合适的转场动画效果，不需要我们做额外的处理，也可以通过之前学习的方法进行指定共享元素转场动画效果。</p>

<h6>组合转场动画</h6>

<p>我们可以把多个转场动画进行组合，作出更具个性的转场效果，在资源文件中通过以下方式：</p>

<pre><code>&lt;transitionSet xmlns:android="http://schemas.android.com/apk/res/android"&gt;
    &lt;explode/&gt;
    &lt;transition class="my.app.transition.CustomTransition"/&gt;
    &lt;&lt;changeImageTransform/&gt; 
&lt;/transitionSet&gt;
</code></pre>

<p>代码中我们可以通过<code>TransitionSet</code>类组合多个转场动画：</p>

<pre><code>TransitionSet transitionSet = new TransitionSet();
transitionSet.addTransition(new Fade());
transitionSet.addTransition(new ChangeBounds());
</code></pre>

<p>组合可以同时针对普通转场动画和共享元素转场动画。</p>

<p>转场动画也可以像普通动画一样设置持续时间，延期执行时间，速率插入器，以及动画的监听等。</p>

<p>转场动画通常是对整个布局起作用，如果我们想对某个特定的view实施转场动画，可以把该view设置为转场动画的target，这样转场动画将只对特定的view起作用。共享元素的动画的target需要指定为transitionName</p>
</body>
</html>